﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Models;
using XmlDataHandler;
using Interfaces;
using System.Drawing;
using System.Windows.Media.Imaging;

namespace Controllers
{
    public class Controller
    {
        #region Fields

        CategoryCollection _categoryCollection;
        BrandCollection _brandCollection;
        GenericItemCollection _genericItemCollection;

        readonly List<ImageModel> _cachedImages;

        #endregion

        #region Creation

        public Controller()
        {
            _cachedImages = new List<ImageModel>();

            using (var adminData = new AdminPanelDataAccess(DataAccessConnection.Open))
            {
                try
                {
                    // instantiate collections
                    _categoryCollection = new CategoryCollection(adminData.AllCategories());
                    _brandCollection = new BrandCollection(adminData.AllBrands());
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }

        #endregion

        #region Public Methods

        // henter generic items der har en given category samt brand tilknyttet
        public void PopulateGenericItemCollection(ICategory category, IBrand brand)
        {
            using (var adminData = new AdminPanelDataAccess(DataAccessConnection.Open))
            {       
                try
                {
                    _genericItemCollection = new GenericItemCollection(adminData.AllGenericItems(category.Id, brand.Id));
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }

        // henter tilhørende items til et generic item
        public void PopulateItemCollection(IGenericItem genericItem)
        {
            using (var adminData = new AdminPanelDataAccess(DataAccessConnection.Open))
            {
                try
                {
                    ((GenericItem)genericItem).AddItems(adminData.ItemsFromGenericItemId(genericItem.Id));
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }

        // hent alle generic items
        public List<IGenericItem> GetGenericItems()
        {
            return _genericItemCollection.GetGenericItems().Select(e => (IGenericItem)e).ToList();
        }

        // hent alle kategorier
        public List<ICategory> GetCategories()
        {
            return _categoryCollection.GetCategories().Select(e => (ICategory)e).ToList();
        }

        // hent alle brands
        public List<IBrand> GetBrands()
        {
            return _brandCollection.GetBrands().Select(e => (IBrand)e).ToList();
        }

        // modtager sti til xml fil, der skal behandles via ImportXML klassen
        public bool SendInput(string pathToFile)
        {
            using (var dataAccess = new XmlImportDataAccess())
            {
                var xmlHandler = new ImportXML(dataAccess);

                // denne metode håndterer alt ved indsættelse og opdateringer
                try
                {
                    // sender stien til xml handleren, som skal håndterer alt logikken
                    xmlHandler.ImportToDatabase(pathToFile);
                    
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }
        }
        
        // modtager sti til billede fil, der skal behandles via ImageHandler klassen
        public bool SendImageInput(string pathToFile, int genericItemId, int itemId)
        {
            using (var dataAccess = new AdminPanelDataAccess())
            {
                var imageHandler = new ImageHandler(dataAccess);
                try
                {
                    // denne metode håndterer redigering af billede, samt uploader til databasen
                    imageHandler.UploadFile(pathToFile, genericItemId, itemId);
                    return true;
                }
                catch (Exception e)
                {
                    return false;
                }
            }
        }

        // henter et billede, baseret et item og generic item
        public BitmapImage GetItemImage(int genericItemId, int itemId)
        {
            using (var dataAccess = new AdminPanelDataAccess(DataAccessConnection.Open))
            {
                try
                {
                    // hent items tilhørende colorId
                    int colorId = dataAccess.GetColorIdByItem(itemId);

                    // tjek om der allerede findes et billede på det efterspurgte item
                    if (ImageIsCached(genericItemId, colorId))
                    {
                        // hvis der findes et billede, så returner det
                        return GetCachedImage(genericItemId, colorId);
                    }

                    var imageHandler = new ImageHandler(dataAccess);

                    // henter billede fra databasen
                    var image = imageHandler.GetItemImage(genericItemId, itemId);

                    // der blev hentet et billede
                    if (image != null)
                    {
                        _cachedImages.Add(image);
                        return image.Image;
                    }
                }
                catch (Exception e)
                {
                    throw e;
                }
                return null;
            }
        }

        // henter itemdescription til et generic item
        public IItemDescription SelectItemDescription(int genericItemId)
        {
            using (var dataAccess = new AdminPanelDataAccess(state: DataAccessConnection.Open))
            {
                try
                {
                    return (IItemDescription)dataAccess.SelectItemDescription(genericItemId);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }


        // opdaterer itemdescription til et generic item
        public bool UpdateItemDescription(int genericItemId, string itemDescription, string material, decimal discountPrice, string displayName)
        {
            using (var dataAccess = new AdminPanelDataAccess(DataAccessConnection.Open))
            {
                try
                {
                    dataAccess.UpdateItemDescription(genericItemId, itemDescription, material, discountPrice, displayName);
                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
        }


        #endregion

        #region Private Methods

        // tjekker om der findes et cached image til et generic item med tilhørende farve
        public bool ImageIsCached(int genericItemId, int colorId)
        {
            var query = _cachedImages.Where(e => e.GenericItemId == genericItemId && e.ColorId == colorId);
            return query.Any();
        }

        // henter cached item
        public BitmapImage GetCachedImage(int genericItemId, int colorId)
        {
            return (_cachedImages.Where(image => image.GenericItemId == genericItemId)
                                .Where(image => image.ColorId == colorId)
                                .Select(image => image.Image)).Single();
        }

        #endregion

        #region Frontend

        // henter alle farver fra databasen
        public List<IColor> GetAllColors()
        {
            using (var dataAccess = new FrontendDataAccess(DataAccessConnection.Open))
            {
                try
                {
                    return dataAccess.AllColors().Select(e => (IColor)e).ToList();
                }
                catch (Exception)
                {
                    return null;
                }
            }
        }

        // henter alle størrelser fra databasen
        public List<ISizeModel> GetAllSizes()
        {
            using (var dataAccess = new FrontendDataAccess(DataAccessConnection.Open))
            {
                try
                {
                    return dataAccess.AllSizes().Select(e => (ISizeModel)e).ToList();
                }
                catch (Exception)
                {
                    return null;
                }
            }
        }

        // henter total antal rækker på en søgning, bruges til pagination i GUI
        public int GetRowCount(int brandId, int categoryId, int sizeId, int colorId,
                               string query, string material, decimal maxPrice,
                               int hasImage, string order)
        {
            using (var dataAccess = new FrontendDataAccess(DataAccessConnection.Open))
            {
                try
                {
                    return dataAccess.GenericItemSearchCount(colorId, brandId, categoryId,
                                                         sizeId, query, material, maxPrice,
                                                         hasImage, order);
                }
                catch (Exception)
                {
                    return 0;
                }
            }
        }

        // Henter søgeresultat fra GUI, bruger offset og limit til pagination
        public IEnumerable<ISearchData> SearchGenericItems(int offset, int limit, int colorId, 
                                                           int brandId, int categoryId, int sizeId, 
                                                           string query, string material, decimal maxPrice, 
                                                           int hasImage, string order)
        {
            // query og material skal endten have en værdi ellers skal de være en tom string
            query = (String.IsNullOrEmpty(query) || String.IsNullOrWhiteSpace(query)) ? "" : query;
            material = (String.IsNullOrEmpty(material) || String.IsNullOrWhiteSpace(material)) ? "" : material;

            // hvis ikke order har den rigtige formatering, skal den være en tom string
            switch (order)
            {
                case "Max pris":
                    order = "pris";
                    break;
                case "Navn":
                    order = "navn";
                    break;
                default:
                    order = "";
                    break;
            }

            using (var dataAccess = new FrontendDataAccess(DataAccessConnection.Open))
            {
                // laver uri til vores default billede, i tilfælde af et item intet billede har
                var uri = new Uri("/Images/missing-image.jpg", UriKind.Relative);

                // generer source til vores default billede
                BitmapSource imageMissing = new BitmapImage(uri);

                //for hvert søgeresultat fra database query
                foreach (var searchItem in dataAccess.GenericItemSearch(offset, limit, colorId, brandId,
                                                                        categoryId, sizeId, query, material, maxPrice,
                                                                        hasImage, order))
                {
                    BitmapSource bitmapSource;

                    // hvis et item har et billede, forsøg da at konverter det til en BitmapSource type
                    if (searchItem.Image != null)
                    {
                        var bitmap = (Bitmap) ImageHandler.StaticByteArrayToImage(searchItem.Image);
                        bitmapSource = ImageHandler.LoadBitmap(bitmap);
                    }
                    else bitmapSource = null;

                    // hvis item har et billede, brug det, ellers default
                    searchItem.DisplayImage = (searchItem.Image != null)
                                                  ? bitmapSource
                                                  : imageMissing;

                    // returnerer søge resultalter en efter en, vha. yield keyword
                    yield return (ISearchData)searchItem;
                }
            }
        }

        #endregion
    }
}
